<template>
  <div class="w-accordion" :class="accordionClasses">
    <div class="w-accordion__item" v-for="(item, i) in accordionItems" :key="i" :class="itemClasses(item)"
      :aria-expanded="item._expanded ? 'true' : 'false'">
      <div class="w-accordion__item-title" @click="!item._disabled && toggleItem(item, $event)"
        @focus="$emit('focus', cleanItem(item))" @keypress.enter="!item._disabled && toggleItem(item, $event)"
        :tabindex="!item._disabled && 0" :class="titleClass">
        <w-button class="w-accordion__expand-icon" v-if="expandIcon && !expandIconRight"
          :icon="(item._expanded && collapseIcon) || expandIcon" :disabled="item._disabled || null" :tabindex="-1" text
          @keypress.stop="!item._disabled && toggleItem(item, $event)"
          @click.stop="!item._disabled && toggleItem(item, $event)"></w-button>
        <slot v-if="$slots[`item-title.${item.id || i + 1}`]" :name="`item-title.${item.id || i + 1}`"
          :item="cleanItem(item)" :expanded="item._expanded" :index="i + 1">1</slot>
        <slot v-else name="item-title" :item="cleanItem(item)" :expanded="item._expanded" :index="i + 1">
          <div class="grow" v-html="item[itemTitleKey]"></div>
        </slot>
        <w-button class="w-accordion__expand-icon" v-if="expandIcon && expandIconRight"
          :icon="(item._expanded && collapseIcon) || expandIcon" text
          @keypress.stop="!item._disabled && toggleItem(item, $event)"
          @click.stop="!item._disabled && toggleItem(item, $event)"></w-button>
      </div>
      <w-transition-expand y>
        <div class="w-accordion__item-content" v-if="item._expanded" :class="contentClass">
          <slot v-if="$slots[`item-content.${item.id || i + 1}`]" :name="`item-content.${item.id || i + 1}`"
            :item="cleanItem(item)" :expanded="item._expanded" :index="i + 1"></slot>
          <slot v-else name="item-content" :item="cleanItem(item)" :expanded="item._expanded" :index="i + 1">
            <div v-html="item[itemContentKey]"></div>
          </slot>
        </div>
      </w-transition-expand>
    </div>
  </div>
</template>

<script>
  import { computed, reactive, watch } from "vue";

  export default {
    name: "w-accordion",

    props: {
      modelValue: { type: Array },
      color: { type: String },
      bgColor: { type: String },
      items: { type: [Array, Number], required: true },
      itemColorKey: { type: String, default: "color" }, // Support a different color per item.
      itemTitleKey: { type: String, default: "title" },
      itemContentKey: { type: String, default: "content" },
      itemClass: { type: String },
      titleClass: { type: String },
      contentClass: { type: String },
      expandIcon: { type: [String, Boolean], default: "wi-triangle-down" },
      expandIconRight: { type: Boolean },
      expandSingle: { type: Boolean },
      collapseIcon: { type: String },
      shadow: { type: Boolean },
    },

    emits: ["input", "update:modelValue", "focus"],

    setup(props, ctx) {
      const accordionItems = computed(() => {
        const items =
          typeof props.items === "number"
            ? Array(props.items).fill({})
            : props.items || [];
        return items.map((item, _index) =>
          reactive({
            ...item,
            _index,
            _expanded: props.modelValue && props.modelValue[_index],
            _disabled: !!item.disabled,
          })
        );
      });
      const accordionClasses = computed(() => {
        return {
          [props.color]: props.color,
          [`${props.bgColor}--bg`]: props.bgColor,
          "w-accordion--shadow": props.shadow,
          "w-accordion--no-icon": !props.expandIcon && !props.collapseIcon,
          "w-accordion--icon-right": props.expandIcon && props.expandIconRight,
          "w-accordion--rotate-icon": props.expandIcon && !props.collapseIcon,
        };
      });

      function toggleItem(item, e) {
        item._expanded = !item._expanded;
        if (props.expandSingle)
          accordionItems.value.forEach(
            (obj) => obj._index !== item._index && (obj._expanded = false)
          );
        const expandedItems = accordionItems.value.map(
          (item) => item._expanded || false
        );
        ctx.emit("update:modelValue", expandedItems);
        ctx.emit("input", expandedItems);

        // When a focused item moves in the page, the scrollTop is naturally updated by the browser.
        // So if expandSingle is set to true, clicking on the next title of an open pane would shift the
        // scrollTop unless unfocused while transitioning. #3.
        e.target.blur();
        setTimeout(() => e.target.focus(), 300);
      }
      function cleanItem(item) {
        // eslint-disable-next-line no-unused-vars
        const { _index, _expanded, _disabled, ...Item } = item;
        return Item;
      }
      function itemClasses(item) {
        return {
          [props.itemClass]: props.itemClass || null,
          "w-accordion__item--expanded": item._expanded,
          "w-accordion__item--disabled": item._disabled,
          [item[props.itemColorKey]]: item[props.itemColorKey],
        };
      }
      watch(
        () => props.modelValue,
        (array) => {
          accordionItems.value.forEach((item, i) => {
            item.expanded = (Array.isArray(array) && array[i]) || false;
          });
        },
        {
          immediate: true, // 绑定时加载
        }
      );
      return {
        accordionItems,
        accordionClasses,
        // methods
        toggleItem,
        cleanItem,
        itemClasses,
      };
    },
  };
</script>